﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Commands;

namespace CommandsTests
{
    [TestFixture]
    public class CommandStringParserTest
    {
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Constructor_NullManager_Exception()
        {
            var parser = new CommandStringParser(null);
        }

        [Test]
        public void Constructor_GoodManager_Ok()
        {
            var parser = _CreateParser();
        }

        [Test]
        public void Parse_NullString_NoCommands()
        {
            var parser = _CreateParser();
            var commands = parser.ParseCommandsString(null);

            Assert.NotNull(commands);
            Assert.IsEmpty(commands);
        }

        [Test]
        public void Parse_OneParameter_Good()
        {
            var mock = new Mock<IMethodsManager>();

            var parameters = new List<ParameterAttribute>();
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par1"));
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par2"));
            mock.Setup(x => x.GetCommandParameters(It.IsAny<string>())).Returns(parameters);

            var parser = new CommandStringParser(mock.Object);

            var commandStr = "commandname2 par1:val28";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(1, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname2", first.Name);

            Assert.AreEqual(1, first.Parameters.Count());

            var parameter = first.Parameters.ElementAt(0);
            Assert.AreEqual("par1", parameter.Name);
            Assert.AreEqual("val28", parameter.Value);
        }

        [Test]
        public void Parse_SeveralParameters_Good()
        {
            var parser = _CreateParser();
            var commandStr = "commandname1 par1:val1 par2: par3:\"adas:3 rte\"";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(1, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname1", first.Name);

            Assert.AreEqual(3, first.Parameters.Count());

            var parameter = first.Parameters.ElementAt(0);
            Assert.AreEqual("par1", parameter.Name);
            Assert.AreEqual("val1", parameter.Value);

            parameter = first.Parameters.ElementAt(1);
            Assert.AreEqual("par2", parameter.Name);
            Assert.AreEqual(string.Empty, parameter.Value);

            parameter = first.Parameters.ElementAt(2);
            Assert.AreEqual("par3", parameter.Name);
            Assert.AreEqual("adas:3 rte", parameter.Value);
        }

        [Test]
        public void Parse_SeveralParametersWithoutNames_Good()
        {
            var parser = _CreateParser();
            var commandStr = "commandname4 val1 val2 val3";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(1, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname4", first.Name);

            Assert.AreEqual(3, first.Parameters.Count());

            var parameter = first.Parameters.ElementAt(0);
            Assert.IsNull(parameter.Name);
            Assert.AreEqual("val1", parameter.Value);

            parameter = first.Parameters.ElementAt(1);
            Assert.IsNull(parameter.Name);
            Assert.AreEqual("val2", parameter.Value);

            parameter = first.Parameters.ElementAt(2);
            Assert.IsNull(parameter.Name);
            Assert.AreEqual("val3", parameter.Value);
        }

        [Test]
        public void Parse_SeveralCommandsSeveralParameters_Good()
        {
            var mock = new Mock<IMethodsManager>();
            var parameters = new List<ParameterAttribute>();
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par1"));
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par2"));
            mock.Setup(x => x.GetCommandParameters(It.IsAny<string>())).Returns(parameters);
            var parser = new CommandStringParser(mock.Object);

            var commandStr = "commandname1 par1:val1 par2: par3:\"adas:3 rte\"";
            commandStr += Environment.NewLine + "commandname2 par1:val28"; 
            commandStr += Environment.NewLine + "commandname3 val42";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(3, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname1", first.Name);

            Assert.AreEqual(3, first.Parameters.Count());

            var parameter = first.Parameters.ElementAt(0);
            Assert.AreEqual("par1", parameter.Name);
            Assert.AreEqual("val1", parameter.Value);

            parameter = first.Parameters.ElementAt(1);
            Assert.AreEqual("par2", parameter.Name);
            Assert.AreEqual(string.Empty, parameter.Value);

            parameter = first.Parameters.ElementAt(2);
            Assert.AreEqual("par3", parameter.Name);
            Assert.AreEqual("adas:3 rte", parameter.Value);

            var second = commands.ElementAt(1);
            Assert.AreEqual("commandname2", second.Name);

            Assert.AreEqual(1, second.Parameters.Count());

            parameter = second.Parameters.ElementAt(0);
            Assert.AreEqual("par1", parameter.Name);
            Assert.AreEqual("val28", parameter.Value);

            var third = commands.Last();
            Assert.AreEqual("commandname3", third.Name);

            Assert.AreEqual(0, third.Parameters.Count());

            var defaultParameter = third.Parameters.DefaultParameter;
            Assert.AreEqual("val42", defaultParameter);
        }
        
        [Test]
        public void Parse_DefaultParameter_Good()
        {
            var parser = _CreateParser();
            var commandStr = "commandname3 val42";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(1, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname3", first.Name);

            Assert.AreEqual(0, first.Parameters.Count());

            var defaultParameter = first.Parameters.DefaultParameter;
            Assert.AreEqual("val42", defaultParameter);
        }

        [Test]
        public void Parse_DefaultParameterMethodHasParameters_Good()
        {
            var mock = new Mock<IMethodsManager>();
            var parameters = new List<ParameterAttribute>();
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par1"));
            parameters.Add(new ParameterAttribute("", typeof(bool), false, "par2"));
            mock.Setup(x => x.GetCommandParameters(It.IsAny<string>())).Returns(parameters);
            var parser = new CommandStringParser(mock.Object);

            var commandStr = "commandname3 val42";
            var commands = parser.ParseCommandsString(commandStr);

            Assert.NotNull(commands);
            Assert.AreEqual(1, commands.Count());

            var first = commands.First();
            Assert.AreEqual("commandname3", first.Name);

            Assert.AreEqual(0, first.Parameters.Count());

            var defaultParameter = first.Parameters.DefaultParameter;
            Assert.AreEqual("val42", defaultParameter);
        }


        private CommandStringParser _CreateParser()
        {
            var mock = new Mock<IMethodsManager>();
            mock.Setup(x => x.GetCommandParameters(It.IsAny<string>()))
                .Returns(new List<ParameterAttribute>());
            return new CommandStringParser(mock.Object);
        }
    }
}
